package com.shop.shopserver.filter;

import com.shop.shopcommon.exception.Base.BloomFilterInitializationException;
import com.shop.shoppojo.entity.Goods;
import com.shop.shopserver.mapper.GoodsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Set;

/**
 * 分层布隆过滤器
 */
@Slf4j
public class HierarchicalBloomFilter {
    private final RedisTemplate <String, String> redisTemplate;
    private final GoodsMapper goodsMapper;

    // 布隆过滤器数组
    private  BloomFilter[] filters;

    // 重新分配层级时的临时布隆过滤器数组
    private BloomFilter[] tempFilters;

    // 阈值数组，表示每个层级的阈值，从大到小排序
    private int[] popularityThresholds;
    // 布隆过滤器访问次数的zset的redis key
    private final String SEARCH_GOODS_COUNT_KEY = "search_goods_count";

    public HierarchicalBloomFilter(int[] sizes, int[] hashCounts, int[] popularityThresholds, RedisTemplate<String, String> redisTemplate, GoodsMapper goodsMapper) {
        this.redisTemplate = redisTemplate;
        this.goodsMapper = goodsMapper;
        log.info("Creating HierarchicalBloomFilter with sizes {} and hashCounts {}", Arrays.toString(sizes), Arrays.toString(hashCounts));
        filters = new BloomFilter[sizes.length];
        for (int i = 0; i < sizes.length; i++) {
            filters[i] = new BloomFilter(sizes[i], hashCounts[i]);
        }
        this.popularityThresholds = popularityThresholds;

        // 初始化临时布隆过滤器数组
        tempFilters = new BloomFilter[sizes.length];
        for (int i = 0; i < tempFilters.length; i++) {
            tempFilters[i] = new BloomFilter(sizes[i], hashCounts[i]);
        }
    }

//    public void add(String item, int index) {
//        filters[index].add(item);
//    }

    public boolean contains(String item) {
        // 先判断遍历层级，查看层级是否存在，不存在则返回false
        for (int i = 0;i<filters.length;i++){
            if (filters[i].contains(item)){
                // 如果存在，则将访问记录+1，并返回true
                // 假设每次访问时将访问记录+1
                log.info("Item {} found in filter level {}", item, i);
                redisTemplate.opsForZSet().incrementScore(SEARCH_GOODS_COUNT_KEY, item, 1);
                return true;
            }
        }
        // 如果不存在，并返回false
        log.info("Item {} not found in any filter level", item);
        return false;
    }

    // 初始化访问次数的zset和布隆过滤器
    public void init() {
        log.info("Initializing HierarchicalBloomFilter...");
        // 如何redis不存在，则初始化zset
        if (Boolean.FALSE.equals(redisTemplate.hasKey(SEARCH_GOODS_COUNT_KEY))) {
            log.info("Redis SEARCH_GOODS_COUNT does not exist, initializing...");
            // 不存在
            // 获取所有商品id
            for (Goods goods : goodsMapper.selectList(null)) {
                redisTemplate.opsForZSet().add(SEARCH_GOODS_COUNT_KEY, goods.getId().toString(), 0);
            }
        } else log.info("Redis SEARCH_GOODS_COUNT exists, skipping initialization.");
        // 初始化布隆过滤器，分配层级
        reset();
    }

    // 定期重置布隆过滤器，根据访问记录重新分配层级
    public void reset() {
        // 获取访问记录条数
        Long goodsCount = redisTemplate.opsForZSet().zCard(SEARCH_GOODS_COUNT_KEY);
        // 分层策略 将zset按访问次数进行分组，并分配到对应层级的布隆过滤器中
        int sum = Arrays.stream(popularityThresholds).sum();
        // 1. 初始化左索引边界
        long leftBound=0;
        // 2. 初始化右索引边界
        long rightBound=-1;
        // 3. 遍历层级，将zset按访问次数进行分组，并分配到对应层级的临时布隆过滤器中
        for (int i = 0; i < popularityThresholds.length; i++) {
            // 3.1 获取当前层级左索引边界
            if (i == 0) {
                leftBound = 0;
            } else {
                // +1 是为了避免重复将一个元素分层问题
                leftBound = (long) Math.floor((double) popularityThresholds[i - 1] /sum*goodsCount+1);
            }
            // 3.2 获取当前层级右索引边界
            if (i == popularityThresholds.length - 1) {
                rightBound = -1;
            } else {
                rightBound = (long) Math.floor((double) popularityThresholds[i] /sum*goodsCount);
            }
            log.info("leftBound:"+leftBound+" rightBound:"+rightBound);
            // 3.3 获取分组区间内的商品id
            Set<String> searchGoodsIds = redisTemplate.opsForZSet().reverseRange(SEARCH_GOODS_COUNT_KEY, leftBound, rightBound);
            log.info("Search goods IDs in level {}: {}", i, searchGoodsIds);
            if (searchGoodsIds == null || searchGoodsIds.isEmpty()) {
                throw new BloomFilterInitializationException("Failed to initialize BloomFilter.");
            }
            // 3.4 分配到对应层级的布隆过滤器中
            for (String searchGoodsId : searchGoodsIds) {
                // 将商品添加到对应层级的临时布隆过滤器中
                log.info("添加商品"+searchGoodsId+"到"+i+"层");
                tempFilters[i].add(String.valueOf(searchGoodsId));
            }
        }
        // 4 将临时布隆过滤器中的商品赋值到对应层级的布隆过滤器中
        for (int i = 0; i < tempFilters.length; i++) {
            filters[i] = tempFilters[i];
        }
    }

    // 定期重建布隆过滤器，根据访问记录重新分配层级
    // 每天凌晨 2 点执行任务
    @Scheduled(cron = "0 0 2 * * ?")
    public void rebuild() {
        log.info("Rebuilding HierarchicalBloomFilter...");
        reset();
    }
}
